home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 426-450 / disk_429 / dr / source / pureio.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  13KB  |  351 lines

  1. /*  PUREIO.C  */
  2.  
  3. #ifdef THIS_IS_A_COMMENT
  4.  
  5. Pureio is a set of vaguely stdio-ish functions for buffered writing to
  6. standard output with reentrancy (in effect), for writing "pure" programs.  It
  7. is efficient and quite compact.  It uses a strange hack to get access to an
  8. address that you don't explicitly pass it with each call.  It is intended for
  9. Aztec C programs, because I think Lattice C can get reentrant IO with the
  10. amiga.lib stdio functions, if I'm not mistaken.
  11.  
  12. Pureio has two versions:  the normal one is synchronous and flushes output
  13. each time it encounters a newline.  The buffer is generally not much more
  14. than one line long in this case; 128 characters is typical.  The other is
  15. asynchronous and flushes output only when the buffer is full or when
  16. requested.  The buffer is often large in this case, like 500 to 5000
  17. characters.  There are actually two buffers, each of the requested size, so
  18. that one can be filling up while another is being written out.
  19.  
  20. To compile the asynchronous version of pureio, #define the symbol
  21. ASYNCHRONICITY (e.g. with the -d option in the cc command).  Compiling this
  22. file without that symbol produces the synchronous version.
  23.  
  24. The asynchronous version offers much less advantage for speed than you might
  25. think.  In writing to CON: particularly, it will save little time, and that
  26. only if the priority of the process using pureio is at least equal to the
  27. priority of the CON handler, which is typically five; otherwise the CON
  28. process hogs the CPU and your program doesn't procede worth a damn.  Even if
  29. what you want to do simultaneously with the CON output is not calculation,
  30. your program won't get far enough to start up that other operation until the
  31. CON action is finished.  (Why doesn't console.device spend more time waiting
  32. on the blitter?)  And it causes output to be uninterruptible by typein or
  33. control-C for many lines at a time, if you use a large buffer.  Thus, the Dr
  34. program, for which this was originally written, uses the synchronous version
  35. now, because the asynchronous offered little improvement.  For the most
  36. efficient overlapping of IO with other stuff, you might try putting a flush
  37. (call the void function pflush()) just before whatever section of prolonged
  38. calculation or disk IO or whatever that you want to have running
  39. simultaneously with the writing of output.
  40.  
  41. ----------------------------------------------------------------------------
  42.  
  43. To use pureio, you must allocate a variable containing fourteen short words
  44. (28 bytes) in your outermost function invocation (equivalent of main).  IT
  45. MUST BE IN SUCH A PLACE THAT THE POSITION IT OCCUPIES ON YOUR STACK NEVER
  46. DIFFERS BETWEEN DIFFERENT RUNS OF YOUR PROGRAM, and be aligned.  It is
  47. actually going to be an instance of the struct called pureioshit below, which
  48. is 28 bytes in size.  Let's say you create that structure in a variable
  49. called purestuff.  You must then initialize pureio by passing the address of
  50. purestuff to the bool function OpenPureIO().  As a second argument, pass a
  51. long integer value giving the size of the buffer that pureio will use, in
  52. bytes.  An upper limit of 10000 bytes is enforced.  Example of initializing
  53. pureio:
  54.  
  55.     _main()    /* avoid stdio startup code by using _main, not main */
  56.     {
  57.     short purbuf[14];
  58.     ... other declarations ...
  59.     if (InitPureIO(purbuf, 500L)) {
  60.         ...fail, exit;
  61.     }
  62.     ... blah blah, you can use pureio functions now ...
  63.     ClosePureIO();
  64.     }
  65.  
  66. You have to call ClosePureIO before exiting.  The functions offered are all
  67. void except OpenPureIO, which returns nonzero if there's insufficient memory.
  68. You can declare it as either short or long.  They are named differently from
  69. the nearest equivalent stdio functions.  You can #define the usual names to
  70. be these if you want, but don't use those in places where you expect a return
  71. value.  The functions are:
  72.  
  73.     BOOL OpenPureIO(short *block, long size)
  74.     void puch(short c)        /* like putchar(), writes one character     */
  75.     void put(char *s)        /* like fputs(s, stdout), writes the string */
  76.     void putfmt(char *fmt, ...)    /* a simple printf, based on RawDoFmt       */
  77.     void pflush(void)        /* flushes buffered output                  */
  78.     void ClosePureIO(void)    /* releases memory, etc; call this last     */
  79.  
  80. We make the argument to puch a short rather than a char just to save trouble.
  81. The use of putfmt is just like regular printf, with the following exceptions.
  82. 1) Whether you use short or long ints, %d and %x and so on always refer to
  83. shorts, and you have to use %ld etc for longs.  (If you use long ints, you'll
  84. have to use %lc to print chars.)  2) There is no %*d feature to specify field
  85. width with an int parm.  3) Dig this:  there is no %u!  Decimal output is
  86. always signed.  4) There is no %o for octal.  5) There is no floating point
  87. formatting of any kind; no %e, %f, or %g.  Left justified fields, padding with
  88. leading zeroes, and maximum lengths for strings with e.g. %.47s are available.
  89.  
  90. Output is actually written whenever the number of unwritten characters
  91. reaches the size of the buffer or whenever you call pflush.  (ClosePureIO
  92. calls pflush.)  In addition, the synchronous version flushes output at the
  93. end of each line.  The asynchronous version waits for the previous output
  94. packet to return before sending out the current one (no double buffering). 
  95. There is no provision for input in this version.  NOTE THAT when output is
  96. written, it calls the function Chk_Abort() to see if control-C has been
  97. pressed lately.  You must set the variable Enable_Abort to either zero or
  98. non-zero only once at the beginning of your program to determine whether this
  99. can call _abort().  Note that the default version of _abort calls _exit and
  100. is not suitable for a pure program, or any that uses _main instead of main. 
  101. Also, pureio will call _abort() directly if the standard output file handle
  102. is null when it's time to output.
  103.  
  104. A note on using _main instead of main.  This makes your Manx program much
  105. smaller, if you are willing to give up stdio functions (e.g. puts, printf,
  106. anything that uses type FILE) and Un*#$%@!x-like io functions (e.g. open,
  107. creat, etc), and use only raw AmigaDOS io funcs.  You can still use sprintf.
  108. Also, if you want to be runnable from Workbench, you must explicitly wait for
  109. the Workbench startup message, and reply it when you exit.  You cannot use
  110. detach.o.  You must not call exit() or _exit(); use DOS Exit.  This means
  111. that you cannot assume that any file will be automatically closed or that any
  112. malloc'd memory will be automatically freed.  You cannot use the stack
  113. checking option unless you do some tricky hacks.  Your argument line will not
  114. be broken into argv-words, but will still be one string.  Also, the feature
  115. to have a WINDOW= tooltype automatically create a CON: window for standard io
  116. won't happen unless you explicitly call the _wb_parse function (see your
  117. Aztec documentation for how to do that).  _wb_parse is pure.  If you use
  118. any floating point libraries, you'll have to explicitly close them.  I think
  119. they get opened atomatically when needed.  **** Except Aztec 5.0d fails to
  120. open mathtrans.library when using FFP floats, I've found!  That's a bug.
  121.  
  122. You can declare your _main like this:
  123.     long _main(alen, aptr) long alen; char *aptr;
  124. aptr points to the command argument line.  IT IS NOT NULL TERMINATED.  alen
  125. gives its length in bytes.  It probably has a newline at the end.  The long
  126. value you return is the program's exit return code.  (That's why it's called
  127. a return code; your program actually returns it just like a function.  The
  128. CLI actually does call your program as a function.)  To parse the arg line
  129. into argv-words, see the source to _cli_parse in the file crt_src/cliparse.c
  130. on the Aztec C disk Aztec3: (version 5.0) or SYS2: (version 3.6); the version
  131. they give expects _exit to free some memory, and is not pure.  It also
  132. expects you to put quotes inside quoted strings with "" instead of *".  My Dr
  133. program contains an example of an alternate parser that uses *" and is pure
  134. and never mistakes "-opt" enclosed in quotes for a real option.
  135.  
  136. This was written in 7/89 for use in the directory listing program Dr, by Paul
  137. Kienitz.  Compileable only with Manx.  ANSI-ized 12/90.
  138.  
  139. #endif THIS_IS_A_COMMENT
  140.  
  141. #include <exec/ports.h>
  142. #include <exec/memory.h>
  143. #include <libraries/dosextens.h>
  144. #include <Paul.h>
  145. #undef put
  146.  
  147.  
  148.  
  149. typedef struct StandardPacket Spak;
  150.  
  151. typedef struct {
  152.     short bufpos;
  153.     bool packetout;
  154.     char *buffer, *otherbuffer;
  155.     long bufsize;
  156.     Spak *pak;
  157.     BPTR koss;
  158.     struct MsgPort *meep;
  159. } pureioshit;
  160.  
  161. private long boofset;
  162.  
  163. /* This is the offset in bytes between where my pr_ReturnAddr points (the
  164. base of my stack) and the start of the buffer struct.  This value must be
  165. constant for all process, though the address arrived at is different for
  166. each one, being a position in that process's stack.  It's a long instead of
  167. a short because that saves two instructions wherever it's used. */
  168.  
  169.  
  170.  
  171. #ifdef ASYNCHRONICITY
  172.  
  173. private void SendPak(register pureioshit *b)
  174. {
  175.     register struct DosPacket *p = &b->pak->sp_Pkt;
  176.     register struct MsgPort *dest = bip(struct FileHandle, b->koss)->fh_Type;
  177.     str temp;
  178.     p->dp_Link = (adr) &b->pak->sp_Msg;
  179.     p->dp_Port = b->meep;
  180.     p->dp_Type = ACTION_WRITE;
  181.     p->dp_Arg1 = b->koss;
  182.     p->dp_Arg2 = (long) b->buffer;
  183.     p->dp_Arg3 = (long) b->bufpos;
  184.     b->pak->sp_Msg.mn_ReplyPort = b->meep;
  185.     temp = b->buffer;
  186.     b->buffer = b->otherbuffer;
  187.     b->otherbuffer = temp;
  188.     if (dest) {            /* don't send if koss = nil: */
  189.     PutMsg(dest, b->pak);
  190.     b->packetout = true;
  191.     }
  192.     b->bufpos = 0;
  193. }
  194.  
  195.  
  196.  
  197. private void WaitPak(register pureioshit *b)
  198. {
  199.     register struct Message *m;
  200.     
  201.     while (b->packetout) {
  202.     WaitPort(b->meep);
  203.     while (m = GetMsg(b->meep))
  204.         if (m == &b->pak->sp_Msg) {
  205.         b->packetout = false;        /* junk mail gets ignored */
  206.         break;
  207.         }
  208.     }
  209. }
  210.  
  211. #endif
  212.  
  213.  
  214.  
  215. private void flushit(register pureioshit *b)
  216. {
  217. #ifdef ASYNCHRONICITY
  218.     if (b->packetout)
  219.     WaitPak(b);
  220.     if (b->bufpos)
  221.     SendPak(b);
  222. #else
  223.     if (b->bufpos && b->koss)
  224.     Write(b->koss, b->buffer, (long) b->bufpos);
  225.     b->bufpos = 0;
  226. #endif
  227.     Chk_Abort();
  228. }
  229.  
  230.  
  231.  
  232. void puch(short c)
  233. {
  234.     register pureioshit *b =
  235.         (adr) (((str) ThisProcess()->pr_ReturnAddr) + boofset);
  236.     import void _abort(void);
  237.  
  238.     if (!b->buffer || !b->koss) {
  239.     _abort();
  240.     return;
  241.     }
  242.     b->buffer[b->bufpos++] = (char) c;
  243. #ifdef ASYNCHRONICITY
  244.     if (b->bufpos >= b->bufsize)
  245. #else
  246.     if (c == '\n' || b->bufpos >= b->bufsize)
  247. #endif
  248.     flushit(b);
  249. }
  250.  
  251.  
  252.  
  253. #asm
  254.     ; void putfmt(format, ...) str format; (whatever) items; ...
  255.  
  256.     public    _putfmt,_LVORawDoFmt,_geta4
  257.  
  258. put1:    ext.w    d0            ; necessary
  259.     beq.s    naw            ; RawDoFmt sends trailing nul
  260.     movem.l    d2/d3/a4/a6,-(sp)
  261.     jsr    _geta4            ; won't hurt large data programs
  262.     move.w    d0,-(sp)
  263.     bsr    _puch            ; this is a glue routine, see
  264.     addq    #2,sp
  265.     movem.l    (sp)+,d2/d3/a4/a6
  266. naw:    rts
  267.  
  268. _putfmt:
  269.     movem.l    a2-a6,-(sp)    ; play it safe
  270.     move.l    24(sp),a0    ; format
  271.     lea    28(sp),a1    ; data items
  272.     lea    put1,a2        ; putting func - no second parm, garbage in a3
  273.     move.l    4,a6
  274.     jsr    _LVORawDoFmt(a6)    ; -522
  275.     movem.l    (sp)+,a2-a6
  276.     rts
  277.  
  278. #endasm
  279.  
  280.  
  281.  
  282. void put(register str s)
  283. {
  284.     while (*s) puch((short) *(s++));
  285. }
  286.  
  287.  
  288.  
  289. void pflush()
  290. {
  291.     flushit((pureioshit *) (((str) ThisProcess()->pr_ReturnAddr) + boofset));
  292. }
  293.  
  294.  
  295.  
  296. long OpenPureIO(register pureioshit *b, ulong s)
  297. {
  298.     register struct Message *m;
  299.     boofset = (str) b - (str) ThisProcess()->pr_ReturnAddr;    /* hack city */
  300.     b->packetout = b->bufpos = 0;    /* set buffer to emptiness */
  301. #ifdef ASYNCHRONICITY
  302.     if (!(b->koss = Output()) || !(b->pak = NewP(Spak))) {
  303. #else
  304.     if (!(b->koss = Output())) {
  305. #endif
  306.     b->buffer = null;
  307.     return (1L);
  308.     }
  309.     if (s > 10000) s = 10000;
  310.     b->bufsize = s;
  311.     if (!(b->buffer = Alloc(s))) {
  312. #ifdef ASYNCHRONICITY
  313.     Free(Spak, b->pak);
  314. #endif
  315.     return (1L);
  316.     }
  317. #ifdef ASYNCHRONICITY
  318.     if (!(b->otherbuffer = Alloc(s))) {
  319.     FreeMem(b->buffer, s);
  320.     Free(Spak, b->pak);
  321.     return (1L);
  322.     }
  323.     b->meep = CreatePort(null, 0L);
  324.     m = &b->pak->sp_Msg;
  325.     m->mn_Length = 68;
  326.     m->mn_Node.ln_Type = NT_MESSAGE;
  327.     m->mn_Node.ln_Pri = 0;
  328.     m->mn_Node.ln_Name = (adr) &b->pak->sp_Pkt;
  329. #endif
  330.     return (0L);
  331. }
  332.  
  333.  
  334.  
  335. void ClosePureIO(void)
  336. {
  337.     register pureioshit *b =
  338.         (adr) (((str) ThisProcess()->pr_ReturnAddr) + boofset);
  339.     if (b->buffer) {
  340.     flushit(b);
  341. #ifdef ASYNCHRONICITY
  342.     WaitPak(b);
  343.     DeletePort(b->meep);
  344.     Free(Spak, b->pak);
  345.     FreeMem(b->otherbuffer, b->bufsize);
  346. #endif
  347.     FreeMem(b->buffer, b->bufsize);
  348.     b->buffer = null;
  349.     }
  350. }
  351.